/* key.c */
#include "key.h"

void Filter_Init(Filter_Handler_T *handler, uint32_t initValue, uint32_t filterCount)
{
    assert(NULL != handler);
    //memset(handler, 0, sizeof(handler));

    /* For Attr. */
    handler->FilterCountMax = filterCount-1U;

    /* For Static. */
    handler->StableValue = initValue;
    handler->NextValue = initValue;
    handler->AccCounterForNextValue = 1U;
}

uint32_t Filter_GetFiltedOutput(Filter_Handler_T *handler, uint32_t curUnfiltedValue)
{
    if (curUnfiltedValue != handler->StableValue)
    {
        if (curUnfiltedValue != handler->NextValue)
        {
            handler->NextValue = curUnfiltedValue;
            handler->AccCounterForNextValue = 1U;
        }
        else /* curUnfiltedValue == handler->NextValue */
        {
            if (handler->AccCounterForNextValue < handler->FilterCountMax)
            {
                handler->AccCounterForNextValue++;
            }
            else /* (handler->AccCounterForNextValue >= handler->FilterCountMax) */
            {
                handler->StableValue = handler->NextValue;
            }
        }
    }
    return handler->StableValue;
}

void Key_Init(Key_Handler_T *handler, uint32_t longDownCountFirst, uint32_t longDownCountStep)
{
    assert(NULL != handler);

    handler->PreState = eKey_State_UnPressed;
    handler->CurState = eKey_State_UnPressed;

    handler->PressedDownLongMaxFirst = longDownCountFirst;
    handler->PressedDownLongMaxContinue = longDownCountStep;

    handler->PressedDownCounter = 0U;
    handler->PressedDownStepCounter = 0U;
}

Key_State_T Key_GetState(Key_Handler_T *handler, bool isKeyDown)
{
    /* Restore the previous state. */
    if (handler->PreState != handler->CurState)
    {
        handler->PreState = handler->CurState;
    }

    /* Check the conditions and transfer to the new state. */
    switch (handler->CurState)
    {
        case eKey_State_UnPressed:
        /*
        在按键未按下的情况下，可能转换到的状态只有两个：
         - 在检测到按键按下的情况下，转入最初的按下状态；
         - 在未检测到按键按下的情况下，继续保持未按下的状态。
        进行一次判断后等待下次按键信号再次进行判断。
        */
            if (isKeyDown) /* 在没有按下的情况下检测到按键被按下，转入按下状态。 */
            {
                handler->CurState = eKey_State_FirstPressedDown;
                handler->PressedDownCounter++; /* 启动按下计数器。 */
            }
            /*
            else
            {
                // 按键未按下，保持现状。
            }
            */
            break;
        case eKey_State_FirstPressedDown:
        /*
        在按键最初被按下的状态下，下一个状态可能有三个：
         - 在检测到按键继续按下并且保持一段足够长的时间之后，可以转换到长按状态；
         - 在检测到按键继续按下但是并未达到长按事件需要的时间前，保持现状；
         - 在任何情况下检测到按键弹起，总是还原到未按下状态。
        */
            if (isKeyDown) /* 在最初的按下之后继续坚持按下，有机会发展为长按事件。 */
            {
                handler->PressedDownCounter++;
                if (handler->PressedDownCounter >= handler->PressedDownLongMaxFirst)
                {
                    handler->CurState = eKey_State_InLongPressedDownA;
                    handler->PressedDownCounter = 0U; /* 此时计数器转为每个连击事件计数。 */
                    handler->PressedDownStepCounter++; /* 连击事件计数器开始计数。 */
                }
                /*
                else
                {
                    // 在检测到按键继续按下，但是并未达长按事件切换点时，保持现状。
                }
                */
            }
            else /* 在第一次按下之后没持续多久就按键弹起了？ */
            {
                handler->CurState = eKey_State_UnPressed; /* 切换为弹起状态。 */
                handler->PressedDownCounter = 0U; /* 清空按键按下计数器。*/
                handler->PressedDownStepCounter = 0U;
            }
            break;
        case eKey_State_InLongPressedDownA:
        /*
        在进入长按状态后，实际上只能等着还原回未按下状态或者保持现状了，但实际上为了检测长按过程中的
        连击过程，必须在长按过程中产生状态切换。为此，这里设计了在长按过程中的A状态和B状态，即使同样
        在长按过程中，仍然可以A状态和B状态之间相互切换，在切换的时刻产生连击事件。此时，在长按A状态
        下可以转入的下一状态从两个变成了三个：
         - 在检测到按键继续按下并保持一段足够长的时间后，达到连击切换点，可以转换到长按B状态；
         - 在检测到按键继续按下但是并未达到下一个连击切换点时，保持现状；
         - 在任何情况下检测到按键弹起，总是还原到未按下状态。
        */
            if (isKeyDown) /* 在连击状态下的按下之后继续坚持按下，有机会发展为另一个连击事件。 */
            {
                handler->PressedDownCounter++;
                if (handler->PressedDownCounter >= handler->PressedDownLongMaxContinue)
                {
                    // 在检测到按键继续按下并保持一段足够长的时间后，达到连击切换点，可以转换到长按B状态
                    handler->CurState = eKey_State_InLongPressedDownB;
                    handler->PressedDownCounter = 0U; /* 此时计数器转为每个连击事件计数。 */
                    handler->PressedDownStepCounter++; /* 连击事件计数器开始计数。 */
                }
                /*
                else
                {
                    // 在检测到按键继续按下，但是并未达到下一个连击切换点时，保持现状。
                }
                */
            }
            else /* 在任何情况下检测到按键弹起，总是还原到未按下状态。 */
            {
                handler->CurState = eKey_State_UnPressed; /* 切换为弹起状态。 */
                handler->PressedDownCounter = 0U; /* 清空按键按下计数器。*/
                handler->PressedDownStepCounter = 0U;
            }
            break;
        /*
        长按B状态同长按A状态在实际上是一个状态，只是在达到连击切换点时相互切换，所以长按A状态和长按B
        状态除了相互切换成为彼此的下一个状态之外，一切的判断过程都是完全相同的。
        */
        case eKey_State_InLongPressedDownB:
            if (isKeyDown) /* 在连击状态下的按下之后继续坚持按下，有机会发展为另一个连击事件。 */
            {
                handler->PressedDownCounter++;
                if (handler->PressedDownCounter >= handler->PressedDownLongMaxContinue)
                {
                    handler->CurState = eKey_State_InLongPressedDownA;
                    handler->PressedDownCounter = 0U; /* 此时计数器转为每个连击事件计数。 */
                    handler->PressedDownStepCounter++; /* 连击事件计数器开始计数。 */
                }
                /*
                else
                {
                    // 在检测到按键继续按下，但是并未达到下一个连击切换点时，保持现状。
                }
                */
            }
            else /* 在任何情况下检测到按键弹起，总是还原到未按下状态。 */
            {
                handler->CurState = eKey_State_UnPressed; /* 切换为弹起状态。 */
                handler->PressedDownCounter = 0U; /* 清空按键按下计数器。*/
                handler->PressedDownStepCounter = 0U;
            }
            break;
        default:
            break;
    }

    return handler->CurState;
}

Key_Event_T Key_GetEvent(Key_Handler_T *handler)
{
    Key_Event_T retEvent = eKey_Event_Nothing;

    if (handler->PreState == handler->CurState)
    {
        retEvent = eKey_Event_Nothing;
    }
    else if ((eKey_State_UnPressed == handler->PreState) 
          && (eKey_State_FirstPressedDown == handler->CurState))
    {
        retEvent = eKey_Event_PressedDownEdge;
    }
    else if ((eKey_State_FirstPressedDown == handler->PreState)
          && (eKey_State_InLongPressedDownA == handler->CurState))
    {
        retEvent = eKey_Event_PressedDownLongFirst;
    }
    else if ( ((eKey_State_InLongPressedDownA == handler->PreState)
           && (eKey_State_InLongPressedDownB == handler->CurState))
           || ((eKey_State_InLongPressedDownB == handler->PreState)
           && (eKey_State_InLongPressedDownA == handler->CurState)) )
    {
        retEvent = eKey_Event_PressedDownLongContinue;
    }
    else if ( (    (eKey_State_InLongPressedDownB == handler->PreState)
                || (eKey_State_InLongPressedDownA == handler->PreState) )
          && (eKey_State_UnPressed == handler->CurState) )
    {
        retEvent = eKey_Event_PressedUpEdgeLong;
    }
    else if ((eKey_State_FirstPressedDown == handler->PreState)
          && (eKey_State_UnPressed == handler->CurState))
    {
        retEvent = eKey_Event_PressedUpEdgeShort;
    }

    return retEvent;
}

